import { FFmpeg } from '@ffmpeg/ffmpeg'
import { fetchFile, toBlobURL } from '@ffmpeg/util'

interface VideoInfo {
  duration: number
  width: number
  height: number
  fps: number
  format: string
}

interface ProcessProgress {
  type: 'loading' | 'processing' | 'extracting' | 'complete'
  progress: number
  message: string
}

class VideoProcessor {
  private ffmpeg: FFmpeg
  private isLoaded: boolean = false
  private onProgress?: (progress: ProcessProgress) => void

  constructor() {
    this.ffmpeg = new FFmpeg()
  }

  /**
   * 设置进度回调
   */
  setProgressCallback(callback: (progress: ProcessProgress) => void) {
    this.onProgress = callback
  }

  /**
   * 初始化 FFmpeg
   */
  async initialize(): Promise<void> {
    if (this.isLoaded) return

    try {
      this.updateProgress('loading', 10, '正在准备 FFmpeg 环境...')
      console.log('开始初始化 FFmpeg...')

      // 尝试多个CDN源
      const cdnSources = [
        'https://unpkg.com/@ffmpeg/core@0.12.10/dist/esm',
        'https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/esm',
        'https://fastly.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/esm'
      ]

      let loadSuccess = false
      let lastError: Error | null = null

      for (let i = 0; i < cdnSources.length; i++) {
        const baseURL = cdnSources[i]
        
        try {
          this.updateProgress('loading', 20 + (i * 20), `正在尝试加载 FFmpeg (源 ${i + 1}/${cdnSources.length})...`)
          console.log(`尝试从 ${baseURL} 加载 FFmpeg`)

          // 设置超时
          const loadPromise = this.ffmpeg.load({
            coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
            wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
          })

          // 30秒超时
          await Promise.race([
            loadPromise,
            new Promise((_, reject) => 
              setTimeout(() => reject(new Error('加载超时')), 30000)
            )
          ])

          loadSuccess = true
          console.log(`成功从 ${baseURL} 加载 FFmpeg`)
          break
        } catch (error) {
          console.warn(`从 ${baseURL} 加载失败:`, error)
          lastError = error as Error
          
          if (i < cdnSources.length - 1) {
            this.updateProgress('loading', 30 + (i * 20), `源 ${i + 1} 失败，尝试下一个源...`)
            await new Promise(resolve => setTimeout(resolve, 1000)) // 等待1秒再试下一个
          }
        }
      }

      if (!loadSuccess) {
        throw lastError || new Error('所有CDN源都加载失败')
      }

      this.isLoaded = true
      this.updateProgress('loading', 100, 'FFmpeg 加载完成')
      
      console.log('FFmpeg 初始化成功')
    } catch (error) {
      console.error('FFmpeg 初始化失败:', error)
      this.updateProgress('loading', 0, 'FFmpeg 加载失败')
      throw new Error(`视频处理器初始化失败: ${error instanceof Error ? error.message : '未知错误'}`)
    }
  }

  /**
   * 获取视频信息
   */
  async getVideoInfo(file: File): Promise<VideoInfo> {
    await this.initialize()
    
    try {
      this.updateProgress('processing', 10, '正在分析视频信息...')

      // 写入文件到 FFmpeg 文件系统
      await this.ffmpeg.writeFile('input.mp4', await fetchFile(file))
      
      this.updateProgress('processing', 30, '正在获取视频详细信息...')

      let videoInfo: VideoInfo | null = null

      // 尝试使用 ffprobe 获取详细信息
      try {
        await this.ffmpeg.exec([
          '-i', 'input.mp4',
          '-v', 'quiet',
          '-print_format', 'json',
          '-show_format',
          '-show_streams',
          'info.json'
        ])

        this.updateProgress('processing', 60, '正在解析视频参数...')

        const infoData = await this.ffmpeg.readFile('info.json')
        const infoText = new TextDecoder().decode(infoData as Uint8Array)
        const info = JSON.parse(infoText)

        const videoStream = info.streams.find((stream: any) => stream.codec_type === 'video')
        if (videoStream) {
          videoInfo = {
            duration: parseFloat(info.format.duration || '0'),
            width: videoStream.width || 0,
            height: videoStream.height || 0,
            fps: this.parseFPS(videoStream.r_frame_rate || '25/1'),
            format: info.format.format_name || 'unknown'
          }
        }
        
        await this.cleanup(['info.json'])
      } catch (probeError) {
        console.warn('ffprobe失败，尝试备用方法:', probeError)
        
        // 备用方法：通过 ffmpeg 转换得到基本信息
        try {
          // 尝试提取第一帧来获取基本信息
          await this.ffmpeg.exec([
            '-i', 'input.mp4',
            '-vframes', '1',
            '-f', 'null',
            '-'
          ])
          
          // 如果成功，我们知道至少视频是可读的
          // 使用一个简单的方法获取时长
          videoInfo = await this.getBasicVideoInfo('input.mp4')
        } catch (basicError) {
          console.error('基本信息获取也失败:', basicError)
        }
      }

      if (!videoInfo) {
        throw new Error('无法解析视频信息')
      }

      this.updateProgress('processing', 100, '视频信息获取完成')
      
      // 清理临时文件
      await this.cleanup(['input.mp4'])
      
      return videoInfo
    } catch (error) {
      console.error('获取视频信息失败:', error)
      await this.cleanup(['input.mp4', 'info.json'])
      throw new Error('无法获取视频信息')
    }
  }

  /**
   * 获取基本视频信息（备用方法）
   */
  private async getBasicVideoInfo(fileName: string): Promise<VideoInfo> {
    // 创建一个临时的HTML video元素来获取基本信息
    return new Promise((resolve, reject) => {
      const video = document.createElement('video')
      video.muted = true
      video.preload = 'metadata'
      
      // 读取文件数据并创建blob URL
      this.ffmpeg.readFile(fileName)
        .then(data => {
          const blob = new Blob([data as Uint8Array], { type: 'video/mp4' })
          const url = URL.createObjectURL(blob)
          video.src = url
          
          video.onloadedmetadata = () => {
            const videoInfo: VideoInfo = {
              duration: video.duration || 0,
              width: video.videoWidth || 0,
              height: video.videoHeight || 0,
              fps: 25, // 默认值
              format: 'video'
            }
            
            URL.revokeObjectURL(url)
            video.remove()
            resolve(videoInfo)
          }
          
          video.onerror = () => {
            URL.revokeObjectURL(url)
            video.remove()
            reject(new Error('无法读取视频元数据'))
          }
        })
        .catch(reject)
    })
  }

  /**
   * 提取视频帧
   */
  async extractFrames(file: File, frameCount: number = 5): Promise<string[]> {
    await this.initialize()
    
    try {
      // 先获取视频信息
      console.log('获取视频信息用于帧提取...')
      const videoInfo = await this.getVideoInfo(file)
      
      this.updateProgress('extracting', 0, '开始提取视频帧...')

      // 重新写入文件（因为之前可能被清理了）
      await this.ffmpeg.writeFile('input.mp4', await fetchFile(file))

      const frames: string[] = []
      const duration = videoInfo.duration
      const fps = videoInfo.fps || 25
      
      console.log(`视频信息: 时长=${duration}s, 帧率=${fps}fps, 总帧数=${Math.floor(duration * fps)}`)
      
      for (let i = 0; i < frameCount; i++) {
        // 使用更精确的时间计算，避开开头和结尾
        const timePosition = (i + 1) / (frameCount + 1)
        const time = duration * timePosition
        const outputName = `frame_${i}.png`
        
        this.updateProgress('extracting', (i / frameCount) * 80, `正在提取第 ${i + 1}/${frameCount} 帧 (${time.toFixed(2)}s)...`)

        try {
          // 使用改进的FFmpeg命令
          await this.ffmpeg.exec([
            '-ss', time.toString(),        // 先定位时间
            '-i', 'input.mp4',            // 再输入文件（更快）
            '-frames:v', '1',             // 只抽取1帧
            '-f', 'image2',               // 输出为图像格式
            '-vf', 'scale=320:-1',        // 保持宽高比缩放
            '-q:v', '2',                  // 高质量
            outputName
          ])

          // 检查文件是否生成成功
          try {
            const frameData = await this.ffmpeg.readFile(outputName)
            const blob = new Blob([frameData as Uint8Array], { type: 'image/png' })
            const frameUrl = URL.createObjectURL(blob)
            frames.push(frameUrl)

            console.log(`第 ${i + 1} 帧提取完成，时间点: ${time.toFixed(2)}s，大小: ${frameData.length} bytes`)
          } catch (readError) {
            console.warn(`读取第 ${i + 1} 帧失败:`, readError)
            // 跳过这一帧，继续处理下一帧
            continue
          }

          // 立即清理这一帧的临时文件以节省内存
          await this.cleanup([outputName])
          
        } catch (extractError) {
          console.warn(`提取第 ${i + 1} 帧失败:`, extractError)
          // 尝试使用备用方法
          try {
            await this.ffmpeg.exec([
              '-i', 'input.mp4',
              '-ss', time.toString(),
              '-vframes', '1',
              '-f', 'image2',
              '-y',  // 覆盖现有文件
              outputName
            ])
            
            const frameData = await this.ffmpeg.readFile(outputName)
            const blob = new Blob([frameData as Uint8Array], { type: 'image/png' })
            const frameUrl = URL.createObjectURL(blob)
            frames.push(frameUrl)
            
            console.log(`第 ${i + 1} 帧备用方法提取成功`)
            await this.cleanup([outputName])
          } catch (backupError) {
            console.error(`第 ${i + 1} 帧备用方法也失败:`, backupError)
            // 跳过这一帧
          }
        }
      }

      this.updateProgress('extracting', 100, `成功提取 ${frames.length}/${frameCount} 帧`)

      // 清理主输入文件
      await this.cleanup(['input.mp4'])

      if (frames.length === 0) {
        throw new Error('没有成功提取到任何帧')
      }

      this.updateProgress('complete', 100, '视频处理完成')
      
      return frames
    } catch (error) {
      console.error('提取视频帧失败:', error)
      // 确保清理所有临时文件
      await this.cleanup(['input.mp4'])
      throw new Error(`提取视频帧失败: ${error instanceof Error ? error.message : '未知错误'}`)
    }
  }

  /**
   * 生成视频缩略图
   */
  async generateThumbnail(file: File, time?: number): Promise<Blob> {
    await this.initialize()
    
    try {
      const videoInfo = await this.getVideoInfo(file)
      const seekTime = time || (videoInfo.duration / 2) // 默认中间位置
      
      this.updateProgress('processing', 0, '正在生成缩略图...')

      await this.ffmpeg.writeFile('input.mp4', await fetchFile(file))
      
      this.updateProgress('processing', 50, '正在截取关键帧...')

      // 生成高质量缩略图
      await this.ffmpeg.exec([
        '-i', 'input.mp4',
        '-ss', seekTime.toString(),
        '-vframes', '1',
        '-q:v', '1',  // 最高质量
        '-vf', 'scale=640:480',  // 更高分辨率
        'thumbnail.jpg'
      ])

      const thumbnailData = await this.ffmpeg.readFile('thumbnail.jpg')
      const blob = new Blob([thumbnailData as Uint8Array], { type: 'image/jpeg' })

      this.updateProgress('processing', 100, '缩略图生成完成')

      // 清理临时文件
      await this.cleanup(['input.mp4', 'thumbnail.jpg'])

      return blob
    } catch (error) {
      console.error('生成缩略图失败:', error)
      throw new Error('生成缩略图失败')
    }
  }

  /**
   * 压缩视频（可选功能）
   */
  async compressVideo(file: File, quality: 'low' | 'medium' | 'high' = 'medium'): Promise<Blob> {
    await this.initialize()
    
    try {
      this.updateProgress('processing', 0, '正在压缩视频...')

      await this.ffmpeg.writeFile('input.mp4', await fetchFile(file))

      const qualitySettings = {
        low: ['-crf', '32', '-preset', 'fast'],
        medium: ['-crf', '28', '-preset', 'medium'],
        high: ['-crf', '23', '-preset', 'slow']
      }

      await this.ffmpeg.exec([
        '-i', 'input.mp4',
        '-c:v', 'libx264',
        '-c:a', 'aac',
        ...qualitySettings[quality],
        '-movflags', '+faststart',
        'output.mp4'
      ])

      const compressedData = await this.ffmpeg.readFile('output.mp4')
      const blob = new Blob([compressedData as Uint8Array], { type: 'video/mp4' })

      this.updateProgress('processing', 100, '视频压缩完成')

      // 清理临时文件
      await this.cleanup(['input.mp4', 'output.mp4'])

      return blob
    } catch (error) {
      console.error('视频压缩失败:', error)
      throw new Error('视频压缩失败')
    }
  }

  /**
   * 清理临时文件
   */
  private async cleanup(files: string[]): Promise<void> {
    for (const file of files) {
      try {
        await this.ffmpeg.deleteFile(file)
      } catch (error) {
        // 忽略删除失败的错误
        console.warn(`删除临时文件失败: ${file}`)
      }
    }
  }

  /**
   * 解析帧率
   */
  private parseFPS(fpsString: string): number {
    const parts = fpsString.split('/')
    if (parts.length === 2) {
      return parseInt(parts[0]) / parseInt(parts[1])
    }
    return parseFloat(fpsString) || 25
  }

  /**
   * 更新处理进度
   */
  private updateProgress(type: ProcessProgress['type'], progress: number, message: string) {
    if (this.onProgress) {
      this.onProgress({ type, progress, message })
    }
  }

  /**
   * 释放资源
   */
  async dispose(): Promise<void> {
    if (this.isLoaded) {
      // FFmpeg 实例会自动清理
      this.isLoaded = false
    }
  }
}

// 导出单例实例
export const videoProcessor = new VideoProcessor()
export type { VideoInfo, ProcessProgress } 